package personal.zq.practice.common.utils;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

@Slf4j
public class SFTPUtils implements AutoCloseable {

    private final String host;
    private final int port;
    private final String username;
    private final String password;

    private final ChannelSftp sftp;

    public SFTPUtils(String host, int port, String username, String password) throws JSchException {
        this.host = host;
        this.port = port;
        this.username = username;
        this.password = password;
        this.sftp = connect();
    }

    /**
     * 连接sftp服务器
     * 使用完毕之后务必调用 #disconnect()
     *
     * @return 连接
     */
    private ChannelSftp connect() throws JSchException {
        ChannelSftp channelSftp;
        try {
            JSch jsch = new JSch();
            jsch.getSession(username, host, port);
            Session sshSession = jsch.getSession(username, host, port);
            sshSession.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            log.info("SFTP Session connected.");
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            channelSftp = (ChannelSftp) channel;
            log.info("Connected to " + host);
        } catch (Exception e) {
            log.error("sftp connect exception", e);
            throw e;
        }
        return channelSftp;
    }

    /**
     * 断开sftp链接
     */
    public void disconnect() {
        try {
            if (sftp != null) {
                log.info(" sftp closing...");
                sftp.getSession().disconnect();
                sftp.disconnect();
            }
        } catch (JSchException e) {
            log.error(" sftp disconnect exception, host = {}", host, e);
        }
    }

    /**
     * 下载文件
     *
     * @param ftpDirectory 下载目录
     * @param ftpFile      下载的文件
     * @param saveFile     存在本地的路径
     */
    public boolean download(String ftpDirectory, String ftpFile, String saveFile) throws IOException, SftpException {
        Path path = Paths.get(saveFile);
        createNewFile(path);
        try (FileOutputStream fileOutputStream = new FileOutputStream(saveFile)) {
            BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);
            sftp.cd(ftpDirectory);
            sftp.get(ftpFile, bos);
            bos.flush();
            return true;
        }
    }

    /**
     * 下载文件
     *
     * @param sftpFilePath 下载的文件完整目录
     * @param saveFile     存在本地的路径
     */
    public boolean download(String sftpFilePath, String saveFile) throws IOException, SftpException {
        int i = sftpFilePath.lastIndexOf('/');
        if (i == -1)
            return false;
        return download(sftpFilePath.substring(0, i), sftpFilePath.substring(i + 1), saveFile);
    }

    /**
     * @param directory
     * @return 指定目录的文件
     * @throws SftpException
     */
    public List<ChannelSftp.LsEntry> fileList(String directory) throws SftpException {
        return fileList(directory, null);
    }

    /**
     * 根据文件名获取文件
     *
     * @param directory   绝对路径
     * @param namePattern 文件名正则
     * @return
     * @throws SftpException
     */
    public List<ChannelSftp.LsEntry> fileList(String directory, String namePattern) throws SftpException {
        List<ChannelSftp.LsEntry> lsEntryList = new ArrayList<>();
        List<ChannelSftp.LsEntry> fileList = sftp.ls(directory);
        for (ChannelSftp.LsEntry file : fileList) {
            String fileName = file.getFilename();
            if (".".equals(fileName) || "..".equals(fileName) || file.getAttrs().isDir()) {
                continue;
            }
            if (StringUtils.hasText(namePattern) && !fileName.matches(namePattern)) {
                continue;
            }
            lsEntryList.add(file);
        }
        return lsEntryList;
    }

    /**
     * 根据文件名获取文件
     *
     * @param directory   绝对路径
     * @param namePattern 文件名正则
     * @return
     * @throws SftpException
     */
    public List<ChannelSftp.LsEntry> directoryList(String directory, String namePattern) throws SftpException {
        List<ChannelSftp.LsEntry> lsEntryList = new ArrayList<>();
        List<ChannelSftp.LsEntry> fileList = sftp.ls(directory);
        for (ChannelSftp.LsEntry file : fileList) {
            String fileName = file.getFilename();
            if (".".equals(fileName) || "..".equals(fileName) || !file.getAttrs().isDir()) {
                continue;
            }
            if (StringUtils.hasText(namePattern) && !fileName.matches(namePattern)) {
                continue;
            }
            lsEntryList.add(file);
        }
        return lsEntryList;
    }

    private void createNewFile(Path file) throws IOException {
        Files.deleteIfExists(file);
        createDirectoryIfNotExists(file.getParent());
        Files.createFile(file);
    }

    private void createDirectoryIfNotExists(Path path) throws IOException {
        if (!Files.exists(path.getParent()) || !Files.isDirectory(path.getParent())) {
            createDirectoryIfNotExists(path.getParent());
        }
        if (Files.notExists(path) || !Files.isDirectory(path)) {
            Files.createDirectory(path);
            log.info(" create Directory success. path = {}", path);
        }
    }


    @Override
    public void close() throws IOException {
        disconnect();
    }
}